import uuid
import time
import threading
from logging import getLogger
logger = getLogger(__name__)

__all__ = ["ReidsLock", "RedisLockWithWatchdog"]

class ReidsLock:
    def __init__(self, redis_client, key, expire=10):
        self.redis = redis_client
        self.key = key
        self.expire = expire
        self.lock_val = str(uuid.uuid4())

    def acquire(self, blocking=True, timeout=None):
        start_time = time.time()
        while True:
            if self.redis.set(self.key, self.lock_val, nx=True, ex=self.expire):
                return True
            if not blocking or (timeout is not None and (time.time() - start_time) >= timeout):
                return False
            time.sleep(0.05)

    def release(self):
        script = """
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
        """
        self.redis.eval(script, 1, self.key, self.lock_val)

    def __enter__(self):
        self.acquire()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()


class RedisLockWithWatchdog:
    """
    已验证，主力使用
    """
    def __init__(self, redis_client, key, expire=10):
        """
        :param redis_client: Redis 连接
        :param key: Redis 锁 key
        :param expire: 锁过期时间（秒）
        """
        self.redis = redis_client
        self.key = key
        self.expire = expire
        self.renew_interval = max(1, int(expire * 0.5) - 1)
        self.lock_val = str(uuid.uuid4())
        self._stop_event = threading.Event()
        self._watchdog_thread = None

    def acquire(self, blocking=True, timeout=None):
        start_time = time.time()
        while True:
            if self.redis.set(self.key, self.lock_val, nx=True, ex=self.expire):
                self._start_watchdog()
                return True

            if not blocking or (timeout is not None and (time.time() - start_time) >= timeout):
                return False

            time.sleep(0.05)

    def release(self):
        self._stop_watchdog()
        unlock_script = """
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
        """
        self.redis.eval(unlock_script, 1, self.key, self.lock_val)

    def _start_watchdog(self):
        def renew_loop():
            # ✅ 使用 Lua 脚本保证原子性
            renew_script = """
            if redis.call("get", KEYS[1]) == ARGV[1] then
                return redis.call("expire", KEYS[1], ARGV[2])
            else
                return 0
            end
            """
            while not self._stop_event.wait(self.renew_interval):
                try:
                    # ✅ 原子性检查并续约
                    result = self.redis.eval(renew_script, 1, self.key, self.lock_val, self.expire)
                    if result == 1:
                        logger.info(f"成功续约锁 {self.key}")
                    else:
                        logger.warning(f"续约失败，锁可能已被其他客户端获取或已释放")
                        # 可以选择记录日志或采取其他措施
                except Exception as e:
                    logger.warning(f"[Watchdog] renew failed: {e}")

        self._watchdog_thread = threading.Thread(target=renew_loop, daemon=True)
        self._watchdog_thread.start()

    def _stop_watchdog(self):
        self._stop_event.set()

    def __enter__(self):
        self.acquire()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()